
/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */


#include "wrk_setproctitle.h"

extern char **environ;
static int              wrk_argc;
static char           **wrk_argv;
static char           **wrk_os_argv;
static char *wrk_os_argv_last;
#if (WRK_SETPROCTITLE_USES_ENV)

/*
 * To change the process title in Linux and Solaris we have to set argv[1]
 * to NULL and to copy the title to the same place where the argv[0] points to.
 * However, argv[0] may be too small to hold a new title.  Fortunately, Linux
 * and Solaris store argv[] and environ[] one after another.  So we should
 * ensure that is the continuous memory and then we allocate the new memory
 * for environ[] and copy it.  After this we could use the memory starting
 * from argv[0] for our process title.
 *
 * The Solaris's standard /bin/ps does not show the changed process title.
 * You have to use "/usr/ucb/ps -w" instead.  Besides, the UCB ps does not
 * show a new title if its length less than the origin command line length.
 * To avoid it we append to a new title the origin command line in the
 * parenthesis.
 */

extern char **environ;

u_char *
wrk_cpystrn(u_char *dst, u_char *src, size_t n)
{
    if (n == 0) {
        return dst;
    }

    while (--n) {
        *dst = *src;

        if (*dst == '\0') {
            return dst;
        }

        dst++;
        src++;
    }

    *dst = '\0';

    return dst;
}

void *
wrk_alloc(size_t size)
{
    void  *p;

    p = malloc(size);

    return p;
}

wrk_int_t
wrk_save_argv(int argc, char *const *argv)
{
#if (WRK_FREEBSD)

    wrk_os_argv = (char **) argv;
    wrk_argc = argc;
    wrk_argv = (char **) argv;

#else
    size_t     len;
    wrk_int_t  i;

    wrk_os_argv = (char **) argv;
    wrk_argc = argc;

    wrk_argv = wrk_alloc((argc + 1) * sizeof(char *));
    if (wrk_argv == NULL) {
        return WRK_ERROR;
    }

    for (i = 0; i < argc; i++) {
        len = wrk_strlen(argv[i]) + 1;

        wrk_argv[i] = wrk_alloc(len);
        if (wrk_argv[i] == NULL) {
            return WRK_ERROR;
        }

        (void) wrk_cpystrn((u_char *) wrk_argv[i], (u_char *) argv[i], len);
    }

    wrk_argv[i] = NULL;

#endif


    return WRK_OK;
}


wrk_int_t
wrk_init_setproctitle()
{
    u_char      *p;
    size_t       size;
    wrk_uint_t   i;

    size = 0;

    for (i = 0; environ[i]; i++) {
        size += wrk_strlen(environ[i]) + 1;
    }

    p = wrk_alloc(size);
    if (p == NULL) {
        return WRK_ERROR;
    }

    wrk_os_argv_last = wrk_os_argv[0];

    for (i = 0; wrk_os_argv[i]; i++) {
        if (wrk_os_argv_last == wrk_os_argv[i]) {
            wrk_os_argv_last = wrk_os_argv[i] + wrk_strlen(wrk_os_argv[i]) + 1;
        }
    }

    for (i = 0; environ[i]; i++) {
        if (wrk_os_argv_last == environ[i]) {

            size = wrk_strlen(environ[i]) + 1;
            wrk_os_argv_last = environ[i] + size;

            wrk_cpystrn(p, (u_char *) environ[i], size);
            environ[i] = (char *) p;
            p += size;
        }
    }

    wrk_os_argv_last--;

    return WRK_OK;
}


void
wrk_setproctitle(char *title)
{
    u_char     *p;

#if (WRK_SOLARIS)

    wrk_int_t   i;
    size_t      size;

#endif

    wrk_os_argv[1] = NULL;

    p = wrk_cpystrn((u_char *) wrk_os_argv[0], (u_char *) "dist-wrk: ",
                    wrk_os_argv_last - wrk_os_argv[0]);

    p = wrk_cpystrn(p, (u_char *) title, wrk_os_argv_last - (char *) p);

#if (WRK_SOLARIS)

    size = 0;

    for (i = 0; i < wrk_argc; i++) {
        size += wrk_strlen(wrk_argv[i]) + 1;
    }

    if (size > (size_t) ((char *) p - wrk_os_argv[0])) {

        /*
         * wrk_setproctitle() is too rare operation so we use
         * the non-optimized copies
         */

        p = wrk_cpystrn(p, (u_char *) " (", wrk_os_argv_last - (char *) p);

        for (i = 0; i < wrk_argc; i++) {
            p = wrk_cpystrn(p, (u_char *) wrk_argv[i],
                            wrk_os_argv_last - (char *) p);
            p = wrk_cpystrn(p, (u_char *) " ", wrk_os_argv_last - (char *) p);
        }

        if (*(p - 1) == ' ') {
            *(p - 1) = ')';
        }
    }

#endif

    if (wrk_os_argv_last - (char *) p) {
        wrk_memset(p, WRK_SETPROCTITLE_PAD, wrk_os_argv_last - (char *) p);
    }

    // wrk_log_debug1(WRK_LOG_DEBUG_CORE, wrk_cycle->log, 0,
    //                "setproctitle: \"%s\"", wrk_os_argv[0]);
}

#endif /* WRK_SETPROCTITLE_USES_ENV */
